#pragma once

#include <cstdint>
#include <chrono>
#include <memory>
#include <cstring>
#include "network/types.h"

class Packet {
 public:
    using Clock = std::chrono::steady_clock;
    using TimePoint = std::chrono::time_point<Clock>;

    /// Constructor function.
    Packet(const uint8_t* data, std::size_t length, TimePoint timestamp = Clock::now());

    /// Deleted copy constructor and assignment, defaulted move constructor and assignment.
    Packet(const Packet&) = delete;
    Packet& operator= (const Packet&) = delete;
    Packet(Packet&&) noexcept = default;
    Packet& operator= (Packet&&) noexcept = default;

    /// Parse the packet.
    bool parse();

    /// Get oriental data.
    const std::uint8_t* data() const { return m_data.get();}
    std::size_t length() const { return m_length;}
    TimePoint timestamp() const { return m_timestamp;}

    /// Get headers of each level.
    const EthHeader* ethHeader() const { return m_ethHeader;};
    const IPv4Header* ipv4Header() const { return m_ipv4Header;};
    const IPv6Header* ipv6Header() const { return m_ipv6Header;};
    const TCPHeader* tcpHeader() const { return m_tcpHeader;};
    const UDPHeader* udpHeader() const { return m_udpHeader;};

    /// Check protocol type.
    bool isIPv4() const { return m_ipv4Header != nullptr;};
    bool isIPv6() const { return m_ipv6Header != nullptr;};
    bool isTCP() const { return m_tcpHeader != nullptr;};
    bool isUDP() const { return m_udpHeader != nullptr;};

    /// Get Application level data.
    const std::uint8_t* payload() const { return m_payload;};
    std::size_t payloadLength() const { return m_payloadLength;};
 private:
    std::unique_ptr<std::uint8_t[]> m_data;      // origin data
    std::size_t m_length;                        // origin package length
    TimePoint m_timestamp;                       // capture timestamp

    /// The protocol header pointers of each level.
    const EthHeader* m_ethHeader = nullptr;
    const IPv4Header* m_ipv4Header = nullptr;
    const IPv6Header* m_ipv6Header = nullptr;
    const TCPHeader* m_tcpHeader = nullptr;
    const UDPHeader* m_udpHeader = nullptr;

    /// Application level data.
    const uint8_t* m_payload = nullptr;
    std::size_t m_payloadLength = 0;
};